﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace psafth.AMB
{
    public class AMBCalculator
    {
        /// <summary>
        /// Returns the DecoderId from a string of data.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        internal static ulong GetDecoderId(byte[] d)
        {
            return HexToLong(d, false);
        }

        /// <summary>
        /// Returns the DecoderId from a string of data.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        internal static double GetSignalStrength(byte[] d)
        {
            return HexToLong(d, false);
        }

        /// <summary>
        /// Parses a string of hex data to a ulong. Uses reversepairs.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        private static ulong HexToLong(byte[] d)
        {
            return HexToLong(d, false);
        }

        /// <summary>
        /// Parses a string of hex data to a ulong. You can reverse the whole array, otherwise just the pairs.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="littleEndian"></param>
        /// <returns></returns>
        private static ulong HexToLong(byte[] d, bool littleEndian)
        {

            if (littleEndian)
            {
                Array.Reverse(d);
            }

            if (d.Length == 2)
                return (ulong)BitConverter.ToInt16(d, 0);

            if (d.Length == 4)
                return (ulong)BitConverter.ToInt32(d, 0);

            if (d.Length == 8)
                return BitConverter.ToUInt64(d, 0);

            return ulong.Parse(BitConverter.ToString(d, 0), System.Globalization.NumberStyles.HexNumber);
        }

        /// <summary>
        ///  Returns the noise from the byte[]
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        internal static ulong GetUlong(byte[] d, bool reverse)
        {
            return HexToLong(d, reverse);
        }

        /// <summary>
        /// Gets the value from the byte[]
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        internal static ulong GetUlong(byte[] d)
        {
            return HexToLong(d, false);
        }

        /// <summary>
        ///  Returns the transponderId from the string of data.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        internal static ulong GetTransponderId(byte[] d)
        {
            return HexToLong(d, false);
        }

        /// <summary>
        /// Gets the passingtime as DateTime.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        internal static DateTime GetPassingTime(byte[] d)
        {
            try
            {
                byte[] a = d.Take(8).ToArray();

                // Get the microseconds since 1970-01-01:00:00
                ulong epoch = HexToLong(a);

                // return a datetime from seconds
                return FromSeconds(epoch);
            }
            catch (Exception)
            {
                // TODO: Implement logging.

                // Return this very moment.
                return DateTime.Now;
            }
        }

        /// <summary>
        /// Returns the value of a datastring. In this case the TimeStamp from the passing data.
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        internal static ulong GetTimeStamp(byte[] d)
        {
            //ulong t = HexToLong(ArrJoiner(s, 4, s.Length - 8), true);
            return HexToLong(d.Skip(d.Length - 8).Take(4).ToArray());
        }

        /// <summary>
        /// Calculates the TimeStamp to seconds.
        /// </summary>
        /// <param name="millionths"></param>
        /// <returns></returns>
        internal static double LapTime(long millionths)
        {
            double seconds = (millionths / 1000000.0);

            return seconds;
        }

        /// <summary>
        /// Returns a double with a given set of decimals
        /// </summary>
        /// <param name="d">The double</param>
        /// <param name="n">Number of decimals</param>
        /// <returns></returns>
        public static double TrimDouble(double d, int n)
        {
            return Math.Round(d, n);
        }


        /// <summary>
        /// Reverses the pair of a array.
        /// </summary>
        /// <param name="byteArray">the array</param>
        /// <returns>the array with reverse pairs.</returns>
        public static byte[] ReversePairs(byte[] byteArray)
        {
            // Loop through the array.
            for (int i = 0; i < byteArray.Length; i++)
            {
                // Check if the index is odd or even. If even, swap with the previous index
                if (i != 0 && i % 2 == 0)
                {
                    byte currentByte = byteArray[i];
                    byte previousByte = byteArray[i - 1];

                    byteArray[i] = previousByte;
                    byteArray[i - 1] = currentByte;
                }
            }
            return byteArray;
        }

        /// <summary>
        /// Calculates a given timestamp(epoch) to a DateTime.
        /// </summary>
        /// <param name="unixTime">timestamp (epoch)</param>
        /// <returns>DateTime.</returns>
        internal static DateTime FromSeconds(ulong unixTime)
        {
            var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            return epoch.AddMilliseconds(unixTime/1000);
        }
    }
}
